package com.chenjinyan.zuul.filter;

import com.alibaba.fastjson.JSON;
import com.chenjinyan.zuul.vo.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;

/**
 * 验证用户名密码正确后，生成一个token，并将token返回给客户端
 * 该类继承自UsernamePasswordAuthenticationFilter，重写了其中的2个方法
 * attemptAuthentication ：接收并解析用户凭证。
 * successfulAuthentication ：用户成功登录后，这个方法会被调用，我们在这个方法里生成token。
 */
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {

    private Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 签名key
     */
    private static final String SIGNING_KEY = "spring-security-@Jwt!&Secret^#";

    private AuthenticationManager authenticationManager;


    public JWTLoginFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }


    /**
     * 接收并解析用户凭证
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest req,
                                                HttpServletResponse res) throws AuthenticationException {
        try {
            User user = new ObjectMapper()
                    .readValue(req.getInputStream(), User.class);

            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            user.getUsername(),
                            user.getPassword(),
                            new ArrayList<>())
            );
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 用户成功登录后，这个方法会被调用，我们在这个方法里生成token
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest req,
                                            HttpServletResponse res,
                                            FilterChain chain,
                                            Authentication auth) throws IOException, ServletException {
        res.setHeader("content-type", "text/json;charset=utf-8");
        res.setCharacterEncoding("utf-8");
        Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
        // 定义存放角色集合的对象
        List roleList = new ArrayList<>();
        for (GrantedAuthority grantedAuthority : authorities) {
            roleList.add(grantedAuthority.getAuthority());
        }
        // 设置过期时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        // 5分钟
        calendar.add(Calendar.MINUTE, 60);
        Date time = calendar.getTime();
        //采用什么算法是可以自己选择的，不一定非要采用HS512
        String token = Jwts.builder()
                .setSubject(auth.getName() + "-" + roleList)
                .setExpiration(time)
                .signWith(SignatureAlgorithm.HS512, SIGNING_KEY)
                .compact();
        // 登录成功后，返回token到header里面
        //res.addHeader("token", token);
        //获取字符流
        PrintWriter writer = null;
        try {
            writer = res.getWriter();
            Map<String, String> map = new HashMap<>();
            map.put("code", "000000");
            map.put("msg", "登陆成功");
            map.put("token", token);
            map.put("data", "");
            String s = JSON.toJSON(map).toString();
            writer.write(s);
        } catch (IOException e) {
            logger.error("response error", e);

        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

}
